#include "zookeeperutil.h"
#include "mprpcapplication.h"

#include <iostream>

// 全局的watcher观察器，调用这个函数的是第三个线程
void global_watcher(zhandle_t *zh, int type, int state, const char *path, void *watcherCtx)
{
    if (type == ZOO_SESSION_EVENT)
    {
        if (state == ZOO_CONNECTED_STATE)
        {
            sem_t *sem = (sem_t *)(zoo_get_context(zh));
            sem_post(sem);
        }
    }
}

zkClient::zkClient()
    : m_zhandle(nullptr)
{
}

zkClient::~zkClient()
{
    // 关闭句柄，释放资源
    if (m_zhandle != nullptr)
        zookeeper_close(m_zhandle);
}

// 连接zkServer
void zkClient::Start()
{
    std::string host = MprpcApplication::GetInstance().GetConfig().GetValue("zookeeperip");
    std::string port = MprpcApplication::GetInstance().GetConfig().GetValue("zookeeperport");
    std::string connstr = host + ":" + port; // zk连接的格式

    // zookeeper_mt 多线程版本
    // zookeeper的API客户端程序提供了三个线程
    // API调用线程，网络I/O线程，watcher回调线程
    m_zhandle = zookeeper_init(connstr.c_str(), global_watcher, 30000, nullptr, nullptr, 0);
    if (m_zhandle == nullptr)
    {
        std::cout << "zookeeper_init error!" << std::endl;
        exit(EXIT_FAILURE);
    }

    sem_t sem;
    // 第二个参数为 0 代表线程间共享
    sem_init(&sem, 0, 0);
    zoo_set_context(m_zhandle, &sem); // 给watcher回调设置参数

    sem_wait(&sem); // 阻塞等待，等待连接成功，因为上面的连接过程是异步的

    // 走到这儿，说明连接已经成功
    std::cout << "zkserver contected!" << std::endl;
}

void zkClient::Create(const char *path, const char *data, int dataLen, int state)
{
    char path_buffer[128];
    int bufferLen = sizeof path_buffer;
    // 避免重复创建，先判断是否存在
    int flag = zoo_exists(m_zhandle, path, 0, nullptr);
    if (ZNONODE == flag)
    {
        // 创建节点
        flag = zoo_create(m_zhandle, path, data, dataLen, &ZOO_OPEN_ACL_UNSAFE, state, path_buffer, bufferLen);
        if (ZOK == flag)
        {
            std::cout << "znode create success, path: " << path << std::endl;
        }
        else
        {
            std::cout << "znode create failed! path: " << path << std::endl;
            exit(EXIT_FAILURE);
        }
    }
}

std::string zkClient::GetData(const char *path)
{
    char buf[64];
    int bufLen = sizeof buf;
    int flag = zoo_get(m_zhandle, path, 0, buf, &bufLen, nullptr);
    if (ZOK != flag)
    {
        std::cout << "get znode errot, path: " << path << std::endl;
        return "";
    }
    else
    {
        return buf;
    }
}
